#!/usr/bin/env node

var MarkdownIt = require('markdown-it');
var hljs = require('highlight.js');
var server = require('http').createServer(httpHandler),
    exec = require('child_process').exec,
    io = require('socket.io').listen(server),
    os = require('os'),
    send = require('send');

// WARNING: By setting this environment variable, anyone on your network may
// run arbitrary code in your browser and read arbitrary files in the working
// directory of the open file!
if (process.env.INSTANT_MARKDOWN_OPEN_TO_THE_WORLD) {
  // Listen on any interface.
  server.listen(8090, onListening).once('error', onServerError);
} else {
  // Listen locally.
  server.listen(8090, '127.0.0.1', onListening).once('error', onServerError);
}

var md = new MarkdownIt({
  html: true,
  linkify: true,
  highlight: function(str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return hljs.highlight(lang, str).value;
      } catch (err) {
        // Do nothing
      }
    } else {
      return str;
    }
  }
});

var lastWrittenMarkdown = '';
function writeMarkdown(body) {
  lastWrittenMarkdown = md.render(body);
  io.sockets.emit('newContent', lastWrittenMarkdown);
}

function readAllInput(input, callback) {
  var body = '';
  input.on('data', function(data) {
    body += data;
    if (body.length > 1e6) {
      throw new Error('The request body is too long.');
    }
  });
  input.on('end', function() {
    callback(body);
  });
}

function addSecurityHeaders(req, res, isIndexFile) {
  var csp = [];

  // Cannot use 'self' because Chrome does not treat 'self' as http://host
  // when the sandbox directive is set.
  var HTTP_HOST = req.headers.host || 'localhost:8090';
  var CSP_SELF = 'http://' + HTTP_HOST;

  if (!process.env.INSTANT_MARKDOWN_ALLOW_UNSAFE_CONTENT) {
    if (isIndexFile) {
      // index.html will drop the scripting capabilities upon load.
      // csp.push('script-src ' + CSP_SELF + " 'unsafe-inline'");
      csp.push('script-src ' + CSP_SELF + " https://* 'unsafe-inline' 'unsafe-eval'");
      csp.push('sandbox allow-scripts allow-modals allow-forms');
    } else {
      csp.push('script-src ');
    }
  }
  if (process.env.INSTANT_MARKDOWN_BLOCK_EXTERNAL) {
    csp.push('default-src data: ' + CSP_SELF);
    csp.push("style-src data: 'unsafe-inline' " + CSP_SELF);
    csp.push('connect-src ' + CSP_SELF + ' ws://' + HTTP_HOST);
  }
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('Content-Security-Policy', csp.join('; '));
  if (isIndexFile) {
    // Never cache the index file, to make sure that changes to the CSP are
    // picked up across soft reloads.
    res.setHeader('Cache-Control', 'no-store');
  }
}

function httpHandler(req, res) {
  switch(req.method)
  {
    case 'GET':
      // Example: /my-repo/raw/master/sub-dir/some.png
      var githubUrl = req.url.match(/\/[^\/]+\/raw\/[^\/]+\/(.+)/);
      if (githubUrl) {
        addSecurityHeaders(req, res, false);
         // Serve the file out of the current working directory
        send(req, githubUrl[1])
         .root(process.cwd())
         .pipe(res);
        return;
      }

      var isIndexFile = /^\/(index\.html)?(\?|$)/.test(req.url);
      addSecurityHeaders(req, res, isIndexFile);

      // Otherwise serve the file from the directory this module is in
      send(req, req.url)
        .root(__dirname)
        .pipe(res);
      break;

    // case 'HEAD':
      // res.writeHead(200);
      // res.end();
      // exec('open -g http://localhost:8090', function(error, stdout, stderr){
        // http.request({port: 8090})
      // });
      // break;

    case 'DELETE':
      io.sockets.emit('die');
      process.exit();
      break;

    case 'PUT':
      readAllInput(req, writeMarkdown);
      res.writeHead(200);
      res.end();
      break;

    default:
  }
}

io.sockets.on('connection', function(sock){
  process.stdout.write('connection established!');
  if (lastWrittenMarkdown) {
    sock.emit('newContent', lastWrittenMarkdown);
  }
});


function onListening() {
  if (os.platform() === 'win32') {
    exec('start /b http://localhost:8090', function(error, stdout, stderr){});
  } else if (os.platform() === 'darwin') {
    exec('open -g http://localhost:8090', function(error, stdout, stderr){});
  } else { // assume unix/linux
    exec('xdg-open http://localhost:8090', function(error, stdout, stderr){});
  }
  readAllInput(process.stdin, function(body) {
    writeMarkdown(body);
  });
  process.stdin.resume();
}

function onServerError(e) {
  if (e.code === 'EADDRINUSE') {
    readAllInput(process.stdin, function(body) {
      // Forward to existing instant-markdown-d server.
      require('http').request({
        hostname: 'localhost',
        port: 8090,
        path: '/',
        method: 'PUT',
      }).end(body);
    });
    process.stdin.resume();
    return;
  }

  // Another unexpected error. Raise it again.
  throw e;
}
